package tracer;

import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;

import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;

import visualizer.KovidaException;
import visualizer.LayeredGraph;

public class Settings {

	public static Collection<String> classNames;
	public static Document config;
	public static HashMap<String, HashMap<Integer, String>> breakPoints;
	public static Collection<String> breakPointClasses;
	public static HashMap<String, HashMap<Integer, String>> methodBreakPoints;
	public static Collection<String> methodBreakPointClasses;
	public static HashMap<String, HashMap<String, String>> methodWatchPoints;
	public static Collection<String> methodWatchPointClasses;
	public static HashMap<String, HashMap<String, String>> methodExitPoints;
	public static Collection<String> methodExitPointClasses;
	public static HashMap<String, HashMap<String, String>> methodEntryPoints;
	public static Collection<String> methodEntryPointClasses;
	public static boolean hideNodesWithoutRef;
	
	
	public static HashMap<String, String> styles;
	/** gibt den Namen der Variable an der bei den tree_list_element fr die Liste als next-Zeiger verwendet wird */
	public static HashMap<String, String> refs_next;
	public static String info;
	
	private static void init(Document doc) {
		Settings.classNames = new HashSet<String>();
		Settings.config = doc;

		Settings.breakPoints = new HashMap<String, HashMap<Integer, String>>();
		Settings.breakPointClasses = new HashSet<String>();
		
		Settings.methodBreakPoints = new HashMap<String, HashMap<Integer, String>>();
		Settings.methodBreakPointClasses = new HashSet<String>();
		
		Settings.methodWatchPoints = new HashMap<String, HashMap<String,String>>();
		Settings.methodWatchPointClasses = new HashSet<String>();
		
		Settings.methodExitPoints = new HashMap<String, HashMap<String,String>>();
		Settings.methodExitPointClasses = new HashSet<String>();

		Settings.methodEntryPoints = new HashMap<String, HashMap<String,String>>();
		Settings.methodEntryPointClasses = new HashSet<String>();
		
		Settings.styles = new HashMap<String, String>();
		Settings.refs_next = new HashMap<String, String>();
		
		Settings.hideNodesWithoutRef = false;
	}

	// CHRISTIAN: MethodExitPoints, MethodWatchPoint, hidenodeswithoutref
	public static void readConfig(String file) throws JDOMException, IOException {
		SAXBuilder builder = new SAXBuilder();
		Document doc = builder.build(file);
		Settings.init(doc);
		Element root = doc.getRootElement();
		@SuppressWarnings("unchecked")
		List<Element> list = root.getChildren();
		
		if(list != null) {
			for(Element e: list) {
				if(e.getName().equals("classes")) {
					readClasses(e);
				}else if(e.getName().equals("hidenodeswithoutref")){
					readHideNodesWithoutRef(e);
				}else if(e.getName().equals("breakpoints")) {
					readBreakpoints(e);
				} else if(e.getName().equals("methodEntryPoints")){
					readMethodEntryPoints(e);
				}else if(e.getName().equals("methodExitPoints")){
					readMethodExitPoints(e);
				}else if(e.getName().equals("methodWatchPoints")){
					readMethodWatchPoints(e);
				}else if(e.getName().equals("info")) {
					readInfo(e);
				}
			}
		}
	}


	/**
	 * Das Element das bergeben wird ist das "<classes>" Element.
	 * Unter diesem Element gibt es die "<class>" Elemente, die die
	 * Namen aller zu betrachteten Klassen beinhalten und darber hinau
	 * Auskunft geben ob die jeweiligen Klassen Datenstrukturen oder
	 * Elemente darstellen.
	 * 
	 * @param "<classes>" Element der XML-Struktur
	 */
	private static void readClasses(Element parent) {
		@SuppressWarnings("unchecked")
		List<Element> list = parent.getChildren();
		
		if(list != null) {
			for(Element e: list) {
				Settings.classNames.add(e.getAttributeValue("name"));
				Settings.styles.put(e.getAttributeValue("name"), e.getAttributeValue("style"));
				if(e.getAttributeValue("style").equals(LayeredGraph.STYLE_TREE_LIST_ELEMENT_SUBGRAPH)
						|| e.getAttributeValue("style").equals(LayeredGraph.STYLE_TREE_LIST_ELEMENT)) {
					refs_next.put(e.getAttributeValue("name"), e.getAttributeValue("ref_next"));
					if(refs_next.get(e.getAttributeValue("name")) == null) {
						throw new KovidaException("Es wurde fuer die Klasse '"+ e.getAttributeValue("name")+"' kein Attribute 'ref_next' in der conf.xml angegeben.");
					}
				}
			}
		}
	}
	/**
	 * Das Element das bergeben wird ist das "<methodEntryPoints>" Element.
	 * Dieses enhlt das Attribute "name" welches angibt auf welche
	 * Java-File sich die breakpoints beziehen.
	 * Unter diesem Element gibt es die "<method>" Elemente, die
	 * jeweils den Methodennamen enthalten von denen eine Momentaufnahme
	 * gemacht werden soll.
	 * 
	 * @param "<methodEntyPoints>" Element der XML-Struktur
	 */
	private static void readMethodEntryPoints(Element parent) {
		@SuppressWarnings("unchecked")
		List<Element> list = parent.getChildren();
		
		if(list != null) {
			Settings.methodEntryPointClasses.add(parent.getAttributeValue("name"));
			
			HashMap<String, String> mps;
			if(Settings.methodEntryPoints.containsKey(parent.getAttributeValue("name"))){
				mps = Settings.methodEntryPoints.get(parent.getAttributeValue("name"));
			}else{
				mps = new HashMap<String, String>();
			}
			
			for(Element e: list) {
				String orderId = e.getAttributeValue("orderId");
				if (orderId != null)
					mps.put(e.getValue(), orderId);	
			}
			Settings.methodEntryPoints.put(parent.getAttributeValue("name"), mps);
		}
		
	}
	
	/**
	 * Das Element das bergeben wird ist das "<methodExitPoints>" Element.
	 * Dieses enhlt das Attribute "name" welches angibt auf welche
	 * Java-File sich die breakpoints beziehen.
	 * Unter diesem Element gibt es die "<method>" Elemente, die
	 * jeweils den Methodennamen enthalten von denen eine Momentaufnahme
	 * gemacht werden soll.
	 * 
	 * @param "<methodExitPoints>" Element der XML-Struktur
	 */
	private static void readMethodExitPoints(Element parent) {
		@SuppressWarnings("unchecked")
		List<Element> list = parent.getChildren();
		
		if(list != null) {
			Settings.methodExitPointClasses.add(parent.getAttributeValue("name"));
			
			HashMap<String, String> mps;
			if(Settings.methodExitPoints.containsKey(parent.getAttributeValue("name"))){
				mps = Settings.methodExitPoints.get(parent.getAttributeValue("name"));
			}else{
				mps = new HashMap<String, String>();
			}
			
			for(Element e: list) {
				String orderId = e.getAttributeValue("orderId");
				if (orderId != null)
					mps.put(e.getValue(), orderId);	
			}
			Settings.methodExitPoints.put(parent.getAttributeValue("name"), mps);
		}
		
	}
	
	/**
	 * Das Element das bergeben wird ist das "<methodWatchPoints>" Element.
	 * Dieses enhlt das Attribute "name" welches angibt auf welche
	 * Java-File sich die breakpoints beziehen.
	 * Unter diesem Element gibt es die "<method>" Elemente, die
	 * jeweils den Methodennamen enthalten von denen eine Momentaufnahme
	 * gemacht werden soll.
	 * 
	 * @param "<methodWatchPoints>" Element der XML-Struktur
	 */
	private static void readMethodWatchPoints(Element parent) {
		@SuppressWarnings("unchecked")
		List<Element> list = parent.getChildren();
		
		if(list != null) {
			Settings.methodWatchPointClasses.add(parent.getAttributeValue("name"));
			// CHRISTIAN: die hier angegebene Klasse auch visualisieren --> 
			// in classes eintragen, wenn noch nicht vorhanden und mit list-element style speichern
			if(!Settings.classNames.contains(parent.getAttributeValue("name").replace(".java", ""))){
				Settings.classNames.add(parent.getAttributeValue("name").replace(".java", ""));
				Settings.styles.put(parent.getAttributeValue("name").replace(".java", ""), "list");
			}
			HashMap<String, String> mps;
			if(Settings.methodWatchPoints.containsKey(parent.getAttributeValue("name"))){
				mps = Settings.methodWatchPoints.get(parent.getAttributeValue("name"));
			}else{
				mps = new HashMap<String, String>();
			}
			for(Element e: list) {
				String orderId = e.getAttributeValue("orderId");
				if (orderId != null)
					mps.put(e.getValue(), orderId);	
			}
			Settings.methodWatchPoints.put(parent.getAttributeValue("name"), mps);
		}
		
	}
	
		
	/**
	 * Das Element das bergeben wird ist das "<breakpoints>" Element.
	 * Dieses enhlt das Attribute "name" welches angibt auf welche
	 * Java-File sich die breakpoints beziehen.
	 * Unter diesem Element gibt es die "<breakpoint>" Elemente, die
	 * jeweils die Quelltextstellen enthalten von denen eine Momentaufnahme
	 * gemacht werden soll.
	 * 
	 * @param "<breakpoints>" Element der XML-Struktur
	 */
	private static void readBreakpoints(Element parent) {
		@SuppressWarnings("unchecked")
		List<Element> list = parent.getChildren();
		
		if(list != null) {
			Settings.breakPointClasses.add(parent.getAttributeValue("name"));
			HashMap<Integer, String> bps = new HashMap<Integer, String>();
			for(Element e: list) {
				String orderId = e.getAttributeValue("orderId");
				if (orderId != null)
					bps.put(new Integer(e.getValue()), orderId);	
			}
			Settings.breakPoints.put(parent.getAttributeValue("name"), bps);
		}
	}
	// CHRISTIAN:
	private static void readHideNodesWithoutRef(Element parent) {
		Settings.hideNodesWithoutRef = Boolean.parseBoolean(parent.getValue());
	}
	
	private static void readInfo(Element parent) {
		Settings.info = parent.getValue();
	}
}

